package support

import (
	"encoding/json"
	"gitee.com/gcom/gbox/errors"
	"gitee.com/gcom/gbox/form"
	"gitee.com/gcom/stockdock/comm"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	zh_trans "github.com/go-playground/validator/v10/translations/zh"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"time"
)

var FormParser = form.NewDecoder()
var Translate ut.Translator
var Validator = validator.New()

/** for validation **/

func init() {
	zhCN := zh.New()
	uni := ut.New(zhCN, zhCN)
	Translate, ok := uni.GetTranslator("zh")
	if !ok {
		comm.Log.Errorf("获取中文翻译失败")
		return
	}

	if err := zh_trans.RegisterDefaultTranslations(Validator, Translate); err != nil {
		comm.Log.Errorf("注册Validator中文翻译失败%v", err)
	}
}

func ParseAndValidQuery(ctx TracerCtx, q interface{}) bool {
	err := FormParser.Decode(q, ctx.Req.URL.Query())
	if err != nil {
		WriteErrEvent(ctx, "解析参数失败", err)
		return false
	}
	err = Validator.Struct(q)
	if err != nil {
		WriteErrEvent(ctx, "校验参数失败", err)
		return false
	}
	return true
}

func ParseAndValid(ctx TracerCtx, values url.Values, q interface{}) bool {
	err := FormParser.Decode(q, values)
	if err != nil {
		ResFail(ctx, 400, "解析参数失败")
		return false
	}
	err = Validator.Struct(q)
	if err != nil {
		ResFail(ctx, 400, "校验参数失败")
		return false
	}
	return true
}

func GetUrlVal(key string, values url.Values) string {
	if v, ok := values[key]; ok {
		return v[0]
	}
	return ""
}

/** methods for event response **/

const CtxKeyEventId = "event-id"

type event struct {
	Ts  time.Time `json:"ts"`
	Msg string    `json:"msg"`
}

func WriteEvent(ctx TracerCtx, msg string) {
	ctx.Logger.Infof(msg)
	if ctx.Req != nil {
		doWriteEvent(ctx, "", event{time.Now(), msg})
	}
}

func WriteErrEvent(ctx TracerCtx, msg string, err error) {
	ctx.Logger.Infof(msg+", %v", err)

	if ctx.Req != nil {
		detail := msg + ", " + errors.Cause(err).Error()
		doWriteEvent(ctx, msg, event{time.Now(), detail})
	}
}

func doWriteEvent(ctx TracerCtx, msg string, data interface{}) {
	eid := 1
	if id, ok := ctx.Values[CtxKeyEventId]; ok {
		eid = id.(int) + 1

	}
	ctx.Values[CtxKeyEventId] = eid
	res := &resMsg{
		Status: true,
		Code:   200,
		Msg:    msg,
		Data:   data,
	}
	ctx.Resp.Write([]byte("id: " + strconv.Itoa(eid) + "\ndata: "))
	json.NewEncoder(ctx.Resp).Encode(res)
	ctx.Resp.Write([]byte("\n\n"))
	ctx.Resp.(http.Flusher).Flush()
}

/** methods for json message response  **/

type resMsg struct {
	Status bool        `json:"status"`
	Code   int         `json:"code"`
	Msg    string      `json:"msg"`
	Data   interface{} `json:"data"`
}

func (m *resMsg) MarshalJSON() ([]byte, error) {
	dataType := ""
	if m.Data != nil {
		dataType = reflect.TypeOf(m.Data).String()
	}

	var ret = &struct {
		Status bool        `json:"status"`
		Code   int         `json:"code"`
		Msg    string      `json:"msg"`
		Data   interface{} `json:"data"`
		Type   string      `json:"type"`
	}{
		Status: m.Status,
		Code:   m.Code,
		Msg:    m.Msg,
		Data:   m.Data,
		Type:   dataType,
	}
	return json.Marshal(ret)
}

func ResMsg(ctx TracerCtx, status bool, code int, msg string, data interface{}) {
	if ctx.Resp == nil {
		return
	}
	res := &resMsg{
		Status: status,
		Code:   code,
		Msg:    msg,
		Data:   data,
	}
	writeRes(ctx, res)
}

func ResOk(ctx TracerCtx, data interface{}) {
	if ctx.Resp == nil {
		return
	}
	res := &resMsg{
		Status: true,
		Code:   200,
		Data:   data,
	}
	writeRes(ctx, res)
}

func ResFail(ctx TracerCtx, code int, msg string) {
	if ctx.Resp == nil {
		return
	}
	res := &resMsg{
		Status: false,
		Code:   code,
		Msg:    msg,
	}
	writeRes(ctx, res)
}

func ResError(ctx TracerCtx, code int, err error) {
	if ctx.Resp == nil {
		return
	}
	msg := ""
	if e, ok := err.(errors.Chain); ok {
		msg = e[0].Prefix
		if msg == "" {
			msg = e[0].Err.Error()
		}
	} else {
		msg = err.Error()
	}
	ResFail(ctx, code, msg)
}

func writeRes(ctx TracerCtx, res *resMsg) {
	w := ctx.Resp
	header := w.Header()
	if _, ok := header["Content-Type"]; !ok {
		header.Set("Content-Type", "application/json;charset=UTF-8")
	}
	json.NewEncoder(w).Encode(res)
}
